{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-11-27T09:43:41.912136Z",
     "start_time": "2019-11-27T09:43:36.414371Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/eagle/anaconda3/envs/dl_py3/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:516: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_qint8 = np.dtype([(\"qint8\", np.int8, 1)])\n",
      "/Users/eagle/anaconda3/envs/dl_py3/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:517: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_quint8 = np.dtype([(\"quint8\", np.uint8, 1)])\n",
      "/Users/eagle/anaconda3/envs/dl_py3/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:518: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_qint16 = np.dtype([(\"qint16\", np.int16, 1)])\n",
      "/Users/eagle/anaconda3/envs/dl_py3/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:519: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_quint16 = np.dtype([(\"quint16\", np.uint16, 1)])\n",
      "/Users/eagle/anaconda3/envs/dl_py3/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:520: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_qint32 = np.dtype([(\"qint32\", np.int32, 1)])\n",
      "/Users/eagle/anaconda3/envs/dl_py3/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:525: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  np_resource = np.dtype([(\"resource\", np.ubyte, 1)])\n",
      "/Users/eagle/anaconda3/envs/dl_py3/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:541: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_qint8 = np.dtype([(\"qint8\", np.int8, 1)])\n",
      "/Users/eagle/anaconda3/envs/dl_py3/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:542: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_quint8 = np.dtype([(\"quint8\", np.uint8, 1)])\n",
      "/Users/eagle/anaconda3/envs/dl_py3/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:543: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_qint16 = np.dtype([(\"qint16\", np.int16, 1)])\n",
      "/Users/eagle/anaconda3/envs/dl_py3/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:544: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_quint16 = np.dtype([(\"quint16\", np.uint16, 1)])\n",
      "/Users/eagle/anaconda3/envs/dl_py3/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:545: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  _np_qint32 = np.dtype([(\"qint32\", np.int32, 1)])\n",
      "/Users/eagle/anaconda3/envs/dl_py3/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:550: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n",
      "  np_resource = np.dtype([(\"resource\", np.ubyte, 1)])\n",
      "/Users/eagle/anaconda3/envs/dl_py3/lib/python3.6/site-packages/konlpy/tag/_okt.py:16: UserWarning: \"Twitter\" has changed to \"Okt\" since KoNLPy v0.4.5.\n",
      "  warn('\"Twitter\" has changed to \"Okt\" since KoNLPy v0.4.5.')\n"
     ]
    }
   ],
   "source": [
    "from __future__ import absolute_import, division, print_function, unicode_literals\n",
    "import json\n",
    "import pickle\n",
    "import torch\n",
    "from gluonnlp.data import SentencepieceTokenizer\n",
    "from model.net import KobertCRF\n",
    "from data_utils.utils import Config\n",
    "from data_utils.vocab_tokenizer import Tokenizer\n",
    "from data_utils.pad_sequence import keras_pad_fn\n",
    "from pathlib import Path"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-11-27T09:39:08.253581Z",
     "start_time": "2019-11-27T09:39:08.237132Z"
    }
   },
   "outputs": [],
   "source": [
    "class DecoderFromNamedEntitySequence():\n",
    "    def __init__(self, tokenizer, index_to_ner):\n",
    "        self.tokenizer = tokenizer\n",
    "        self.index_to_ner = index_to_ner\n",
    "\n",
    "    def __call__(self, list_of_input_ids, list_of_pred_ids):\n",
    "        input_token = self.tokenizer.decode_token_ids(list_of_input_ids)[0]\n",
    "        pred_ner_tag = [self.index_to_ner[pred_id] for pred_id in list_of_pred_ids[0]]\n",
    "\n",
    "        # ----------------------------- parsing list_of_ner_word ----------------------------- #\n",
    "        list_of_ner_word = []\n",
    "        entity_word, entity_tag, prev_entity_tag = \"\", \"\", \"\"\n",
    "        for i, pred_ner_tag_str in enumerate(pred_ner_tag):\n",
    "            if \"B-\" in pred_ner_tag_str:\n",
    "                entity_tag = pred_ner_tag_str[-3:]\n",
    "\n",
    "                if prev_entity_tag != entity_tag and prev_entity_tag != \"\":\n",
    "                    list_of_ner_word.append({\"word\": entity_word.replace(\"▁\", \" \"), \"tag\": prev_entity_tag, \"prob\": None})\n",
    "\n",
    "                entity_word = input_token[i]\n",
    "                prev_entity_tag = entity_tag\n",
    "            elif \"I-\"+entity_tag in pred_ner_tag_str:\n",
    "                entity_word += input_token[i]\n",
    "            else:\n",
    "                if entity_word != \"\" and entity_tag != \"\":\n",
    "                    list_of_ner_word.append({\"word\":entity_word.replace(\"▁\", \" \"), \"tag\":entity_tag, \"prob\":None})\n",
    "                entity_word, entity_tag, prev_entity_tag = \"\", \"\", \"\"\n",
    "\n",
    "\n",
    "        # ----------------------------- parsing decoding_ner_sentence ----------------------------- #\n",
    "        decoding_ner_sentence = \"\"\n",
    "        is_prev_entity = False\n",
    "        prev_entity_tag = \"\"\n",
    "        is_there_B_before_I = False\n",
    "\n",
    "        for i, (token_str, pred_ner_tag_str) in enumerate(zip(input_token, pred_ner_tag)):\n",
    "            if i == 0 or i == len(pred_ner_tag)-1: # remove [CLS], [SEP]\n",
    "                continue\n",
    "            token_str = token_str.replace('▁', ' ')  # '▁' 토큰을 띄어쓰기로 교체\n",
    "\n",
    "            if 'B-' in pred_ner_tag_str:\n",
    "                if is_prev_entity is True:\n",
    "                    decoding_ner_sentence += ':' + prev_entity_tag+ '>'\n",
    "\n",
    "                if token_str[0] == ' ':\n",
    "                    token_str = list(token_str)\n",
    "                    token_str[0] = ' <'\n",
    "                    token_str = ''.join(token_str)\n",
    "                    decoding_ner_sentence += token_str\n",
    "                else:\n",
    "                    decoding_ner_sentence += '<' + token_str\n",
    "                is_prev_entity = True\n",
    "                prev_entity_tag = pred_ner_tag_str[-3:] # 첫번째 예측을 기준으로 하겠음\n",
    "                is_there_B_before_I = True\n",
    "\n",
    "            elif 'I-' in pred_ner_tag_str:\n",
    "                decoding_ner_sentence += token_str\n",
    "\n",
    "                if is_there_B_before_I is True: # I가 나오기전에 B가 있어야하도록 체크\n",
    "                    is_prev_entity = True\n",
    "            else:\n",
    "                if is_prev_entity is True:\n",
    "                    decoding_ner_sentence += ':' + prev_entity_tag+ '>' + token_str\n",
    "                    is_prev_entity = False\n",
    "                    is_there_B_before_I = False\n",
    "                else:\n",
    "                    decoding_ner_sentence += token_str\n",
    "\n",
    "        return list_of_ner_word, decoding_ner_sentence"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-11-27T09:39:08.265484Z",
     "start_time": "2019-11-27T09:39:08.256436Z"
    }
   },
   "outputs": [],
   "source": [
    "def main():\n",
    "    model_dir = Path('./experiments/base_model_with_crf')\n",
    "    model_config = Config(json_path=model_dir / 'config.json')\n",
    "\n",
    "    # load vocab & tokenizer\n",
    "    tok_path = \"./tokenizer_78b3253a26.model\"\n",
    "    ptr_tokenizer = SentencepieceTokenizer(tok_path)\n",
    "\n",
    "    with open(model_dir / \"vocab.pkl\", 'rb') as f:\n",
    "        vocab = pickle.load(f)\n",
    "    tokenizer = Tokenizer(vocab=vocab, split_fn=ptr_tokenizer, pad_fn=keras_pad_fn, maxlen=model_config.maxlen)\n",
    "\n",
    "    # load ner_to_index.json\n",
    "    with open(model_dir / \"ner_to_index.json\", 'rb') as f:\n",
    "        ner_to_index = json.load(f)\n",
    "        index_to_ner = {v: k for k, v in ner_to_index.items()}\n",
    "\n",
    "    # model\n",
    "    model = KobertCRF(config=model_config, num_classes=len(ner_to_index), vocab=vocab)\n",
    "\n",
    "    # load\n",
    "    model_dict = model.state_dict()\n",
    "    checkpoint = torch.load(\"./experiments/base_model_with_crf/best-epoch-16-step-1500-acc-0.993.bin\", map_location=torch.device('cpu'))\n",
    "    # checkpoint = torch.load(\"./experiments/base_model_with_crf_val/best-epoch-12-step-1000-acc-0.960.bin\", map_location=torch.device('cpu'))\n",
    "    convert_keys = {}\n",
    "    for k, v in checkpoint['model_state_dict'].items():\n",
    "        new_key_name = k.replace(\"module.\", '')\n",
    "        if new_key_name not in model_dict:\n",
    "            print(\"{} is not int model_dict\".format(new_key_name))\n",
    "            continue\n",
    "        convert_keys[new_key_name] = v\n",
    "\n",
    "    model.load_state_dict(convert_keys)\n",
    "    model.eval()\n",
    "    device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')\n",
    "    model.to(device)\n",
    "    decoder_from_res = DecoderFromNamedEntitySequence(tokenizer=tokenizer, index_to_ner=index_to_ner)\n",
    "\n",
    "    while(True):\n",
    "        input_text = input('input> ')\n",
    "        if input_text == 'end':\n",
    "            break\n",
    "        \n",
    "        list_of_input_ids = tokenizer.list_of_string_to_list_of_cls_sep_token_ids([input_text])\n",
    "        x_input = torch.tensor(list_of_input_ids).long()\n",
    "        list_of_pred_ids = model(x_input)\n",
    "\n",
    "        list_of_ner_word, decoding_ner_sentence = decoder_from_res(list_of_input_ids=list_of_input_ids, list_of_pred_ids=list_of_pred_ids)\n",
    "        print(\"output>\", decoding_ner_sentence)\n",
    "        print(\"\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2019-11-27T09:39:02.740Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "input> 영화 ‘겨울왕국2’(감독 크리스 벅, 제니퍼 리)가 개봉 첫 주말에 압도적인 흥행 기록을 세우며, 4일 연속 박스오피스 1위를 지켰다.  25일 영화진흥위원회 영화관 입장권 통합 전산망에 따르면 ‘겨울왕국2’는 지난 24일 2648개 스크린에서 하루 153만5598명을 동원했다. 누적관객수는 443만7947명이다.\n",
      "output>  영화 ‘<겨울왕국2:POH>’(감독 <크리스 벅:PER>, <제니퍼 리:PER>)가 개봉 첫 주말에 압도적인 흥행 기록을 세우며, <4일:DAT> 연속 <박:ORG>스오피스 <1위를:NOH> 지켰다. <25일:DAT> <영화진흥위원회:ORG> 영화관 입장권 통합 전산망에 따르면 ‘<겨울왕국2:POH>’는 <지난:DAT> <24일:DAT> <2648개:NOH> 스크린에서 하루 <153만5598명을:NOH> 동원했다. 누적관객수는 <443만7947명:NOH>이다.\n",
      "\n",
      "input> 국민 애니메이션으로 거듭난 '겨울왕국2'. OST 역시 뜨거운 관심을 받고 있다. 특히 엘사가 부른 'Show yourself(쇼 유어셀프)'는 포털사이트 상위권을 점령하며 'Let It Go(렛 잇 고)' 못지않은 인기를 끌고 있다.   'Show yourself'는 '겨울왕국2'에서 엘사가 자신의 진정한 목적의식을 찾게 되는 순간 부르는 노래다. 'Let It Go' 등 전편 OST에 이어 이번 작업에도 참여한 크리스틴 앤더슨·로페즈 부부는 \"가장 크게 고민했던 점은 '이 음악을 통해 어떤 이야기를 전달해야 하는 것'인가였다\"며 \"캐릭터들의 커다란 감정의 동요와 변화가 말로 쉽게 전달이 안 될 때, 이를 표현할 수 있는 상황과 이야기를 음악으로 탄생시켰다\"고 설명했다.\n",
      "output>  국민 애니메이션으로 거듭난 '<겨울왕국2:POH>'. OST 역시 뜨거운 관심을 받고 있다. 특히 <엘사가:PER> 부른 '<Show yourself:POH>(<쇼 유어셀프:POH>)'는 포털사이트 상위권을 점령하며 '<Let It Go:POH>(<렛 잇 고:POH>)' 못지않은 인기를 끌고 있다. '<Show yourself:POH>'는 '<겨울왕국2:POH>'에서 <엘사가:PER> 자신의 진정한 목적의식을 찾게 되는 순간 부르는 노래다. '<Let It Go:POH>' 등 전편 OST에 이어 이번 작업에도 참여한 <크리스틴:POH> <앤더슨:PER>·로페즈 <부부:POH>는 \"가장 크게 고민했던 <점:POH>은 '이 음악을 통해 어떤 이야기를 전달해야 하는 것'인가였다\"며 \"<캐:POH><릭터:POH>들의 <커다:POH>란 감정의 <동:POH>요와 변화가 말로 쉽게 전달이 <안:POH> 될 때, 이를 표현할 <수:POH> 있는 상황과 이야기를 음악으로 탄생시켰다\"고 설명했다.\n",
      "\n",
      "input> 국민 애니메이션으로 거듭난 '겨울왕국2'. OST 역시 뜨거운 관심을 받고 있다. 특히 엘사가 부른 'Show yourself(쇼 유어셀프)'는 포털사이트 상위권을 점령하며 'Let It Go(렛 잇 고)' 못지않은 인기를 끌고 있다.   'Show yourself'는 '겨울왕국2'에서 엘사가 자신의 진정한 목적의식을 찾게 되는 순간 부르는 노래다.\n",
      "output>  국민 애니메이션으로 거듭난 '<겨울왕국2:POH>'. OST 역시 뜨거운 관심을 받고 있다. 특히 <엘사가:PER> 부른 '<Show yourself:POH>(<쇼 유어셀프:POH>)'는 포털사이트 상위권을 점령하며 '<Let It Go:POH>(<렛 잇 고:POH>)' 못지않은 인기를 끌고 있다. '<Show yourself:POH>'는 '<겨울왕국2:POH>'에서 <엘:PER>사가 자신의 진정한 목적의식을 찾게 되는 순간 부르는 노래다.\n",
      "\n",
      "input> 'Show yourself'는 '겨울왕국2'에서 엘사가 자신의 진정한 목적의식을 찾게 되는 순간 부르는 노래다. 'Let It Go' 등 전편 OST에 이어 이번 작업에도 참여한 크리스틴 앤더슨·로페즈 부부는 \"가장 크게 고민했던 점은 '이 음악을 통해 어떤 이야기를 전달해야 하는 것'인가였다\"며 \"캐릭터들의 커다란 감정의 동요와 변화가 말로 쉽게 전달이 안 될 때, 이를 표현할 수 있는 상황과 이야기를 음악으로 탄생시켰다\"고 설명했다.\n",
      "output>  <'Show yourself:POH>'는 '<겨울왕국2:POH>'에서 <엘:PER>사가 자신의 진정한 목적의식을 찾게 되는 순간 부르는 노래다. '<Let It Go:POH>' 등 전편 OST에 이어 이번 작업에도 참여한 <크리스틴 앤더슨:PER>·<로페즈:PER> 부부는 \"가장 크게 고민했던 점은 '이 음악을 통해 어떤 이야기를 전달해야 하는 것'인가였다\"며 \"캐릭터들의 커다란 감정의 동요와 변화가 말로 쉽게 전달이 안 될 때, 이를 표현할 수 있는 상황과 이야기를 음악으로 탄생시켰다\"고 설명했다.\n",
      "\n",
      "input> 영화 '겨울왕국2'의 이현민 애니메이션 슈퍼바이저가 SBS '나이트라인'에 출연해 다양한 이야기를 전했다. 숨겨진 과거의 비밀과 새로운 운명을 찾기 위해 모험을 떠나는 '엘사'와 '안나'의 이야기를 담은 작품이다.\n",
      "output>  영화 '<겨울왕국2:POH>'의 <이현민:PER> 애니메이션 슈퍼바이저가 <SBS:POH> '<나이트라인:POH>'에 출연해 다양한 이야기를 전했다. 숨겨진 과거의 비밀과 새로운 운명을 찾기 위해 모험을 떠나는 '<엘사:PER>'와 '<안나:PER>'의 이야기를 담은 작품이다.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "main()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "dl_py3",
   "language": "python",
   "name": "dl_py3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
